﻿// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace System.Drawing.Design;

internal abstract partial class SelectionPanelBase : Control
{
    private bool _allowExit = true;
    private RadioButton? _checkedControl;
    private IWindowsFormsEditorService? _editorService;

    protected RadioButton CheckedControl
    {
        get => _checkedControl!;
        set
        {
            _checkedControl = value;
            FocusCheckedControl();
        }
    }

    protected abstract ControlCollection SelectionOptions { get; }

    public object? Value { get; protected set; }

    public void End()
    {
        _editorService = null;
        Value = null;
    }

    private void FocusCheckedControl()
    {
        // To actually move focus to a radio button, we need to call Focus() method.
        // However, that would raise OnClick event, which would close the editor.
        // We set allowExit to false, to block editor exit, on radio button selection change.
        _allowExit = false;
        CheckedControl.Focus();
        _allowExit = true;
    }

    private void OnClick(object? sender, EventArgs e)
    {
        if (_allowExit)
        {
            CheckedControl = (RadioButton)sender!;
            UpdateValue();
            Teardown();
        }
    }

    protected override void OnGotFocus(EventArgs e)
    {
        base.OnGotFocus(e);
        FocusCheckedControl();
    }

    private void OnKeyDown(object? sender, KeyEventArgs e)
    {
        var radioButton = (RadioButton)sender!;
        Keys key = e.KeyCode;
        RadioButton target;

        switch (key)
        {
            case Keys.Up:
                target = ProcessUpKey(radioButton);
                break;
            case Keys.Down:
                target = ProcessDownKey(radioButton);
                break;
            case Keys.Left:
                target = ProcessLeftKey(radioButton);
                break;
            case Keys.Right:
                target = ProcessRightKey(radioButton);
                break;
            case Keys.Return:
                // Will tear down editor
                InvokeOnClick(radioButton, EventArgs.Empty);
                return;
            default:
                return;
        }

        e.Handled = true;

        if (target != sender)
        {
            CheckedControl = target;
        }
    }

    protected override bool ProcessDialogKey(Keys keyData)
    {
        if ((keyData & Keys.KeyCode) == Keys.Tab && (keyData & (Keys.Alt | Keys.Control)) == 0)
        {
            CheckedControl = ProcessTabKey(keyData);
            return true;
        }

        return base.ProcessDialogKey(keyData);
    }

    protected abstract RadioButton ProcessDownKey(RadioButton checkedControl);

    protected abstract RadioButton ProcessLeftKey(RadioButton checkedControl);

    protected abstract RadioButton ProcessRightKey(RadioButton checkedControl);

    protected abstract RadioButton ProcessTabKey(Keys keyData);

    protected abstract RadioButton ProcessUpKey(RadioButton checkedControl);

    protected abstract void SetInitialCheckedControl();

    public void Start(IWindowsFormsEditorService edSvc, object? value)
    {
        foreach (RadioButton radioButton in SelectionOptions)
        {
            radioButton.Checked = false;
        }

        _editorService = edSvc;
        Value = value;

        SetInitialCheckedControl();
        CheckedControl.Checked = true;
    }

    protected void ConfigureButtons()
    {
        foreach (RadioButton radioButton in SelectionOptions)
        {
            radioButton.Click += OnClick;
            radioButton.KeyDown += OnKeyDown;
        }
    }

    private void Teardown()
    {
        _editorService!.CloseDropDown();
    }

    protected abstract void UpdateValue();
}
